import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

axios.defaults.withCredentials = true
import qs from 'qs'

export const loginUrl = import.meta.env.VITE_RENWAL_LOGIN_DOMAIN
export interface BaseResponse<T = any> {
  result: {
    traceId?: string
    status: number
    code?: number
    msg?: string
    stat?: number
    data: T
  }
}
export interface BaseSimpleResponse<T = any> {
  traceId?: string
  status: number
  code?: number
  msg: string
  data: T
}
export interface BaseChangeCourseResponse<T = any> {
  result: {
    code?: number
    status: number
    currentTime?: number
    data: T
  }
}

export interface BaseChangeCourseSimpleResponse<T = any> {
  trace_id?: string
  msg?: string
  stat: number
  data: T
}
export interface cancelResponse<V = any> {
  cancel: () => void
  data: Promise<AxiosResponse<BaseResponse<V>>>
}

const ErrorDic: {
  [key: number]: string
} = {
  400: '错误的请求',
  401: '未授权，请登录',
  403: '服务器拒绝访问',
  404: '服务器未找到资源',
  408: '请求超时',
  500: '服务器内部错误',
  502: '网关错误',
  503: '服务不可用',
  504: '网关超时',
}

class ApiError extends Error {
  constructor(public code = 0, message = '') {
    super(message)
    this.name = this.constructor.name
  }

  static createBuiltInApiError(statusCode: number) {
    const message = ErrorDic[statusCode]
    if (message) {
      return new ApiError(statusCode, message)
    } else {
      return new ApiError(statusCode, '抱歉出错了')
    }
  }
}

export enum RequestContentType {
  URL_ENCODE = 0,
  JSON = 1,
  FORM_DATA = 2,
  FORM_URLENCODED = 3,
}

interface MindRequestConfig {
  withCredentials?: boolean
  contentType?: RequestContentType
  signal?: AbortSignal
}
const formUrlencoded = 'application/x-www-form-urlencoded'
const formDataContentType = 'multipart/form-data'
const defaultRequestConfig: AxiosRequestConfig = {
  timeout: 20000,
  headers: {},
}

function getInstanceConfig(instanceConfig: AxiosRequestConfig): AxiosRequestConfig {
  const { timeout, baseURL, headers } = instanceConfig
  let mergedHeaders = defaultRequestConfig.headers
  if (instanceConfig.headers) {
    mergedHeaders = { ...defaultRequestConfig.headers, ...instanceConfig.headers }
  }
  return { ...defaultRequestConfig, timeout, baseURL, ...mergedHeaders }
}

type InterceptorFulfill<V> = (value: V) => V | Promise<V>
type InterceptorReject = (error: any) => any

export class AjaxRequest {
  private _axiosInstanceConfig: AxiosRequestConfig
  public _ajax: AxiosInstance

  constructor(instanceConfig: AxiosRequestConfig = {}) {
    this._axiosInstanceConfig = getInstanceConfig(instanceConfig)
    this._ajax = axios.create(this._axiosInstanceConfig)

    this._ajax.interceptors.response.use(
      (res: any) => {
        try {
          if (res && res.status === 200) {
            return res
          } else if (res && res.status === 401) {
            localStorage.clear()
            window.location.href = loginUrl + encodeURIComponent(window.location.href)
          } else {
            throw new ApiError(0, '抱歉出错了')
          }
        } catch (err) {
          throw err
        }
      },
      (error) => {
        let errorInstance = null
        if (error.response) {
          errorInstance = ApiError.createBuiltInApiError(error.response.status)
        } else {
          errorInstance = new ApiError(0, '抱歉出错了')
        }

        // throw error
        return Promise.reject(errorInstance)
      },
    )
  }

  registerRequestInterceptor(
    fulfilled: InterceptorFulfill<AxiosRequestConfig>,
    rejected: InterceptorReject,
  ) {
    if (fulfilled) {
      this._ajax.interceptors.request.use(fulfilled, rejected)
    }
  }

  registerResponseInterceptor<V>(
    fulfilled: InterceptorFulfill<AxiosResponse<V>>,
    rejected: InterceptorReject,
  ) {
    if (fulfilled) {
      this._ajax.interceptors.response.use(fulfilled, rejected)
    }
  }

  changeAxiosHeaderCommon(headerText: string, value: string): void {
    if (headerText !== '') {
      axios.defaults.headers.common[headerText] = value
    }
  }

  async get<V extends BaseResponse | any>(
    url: string,
    params: any,
    { withCredentials = true }: MindRequestConfig = {},
  ): Promise<AxiosResponse<V>> {
    const resultParams = params || {}
    const pro = this._ajax.get<V, AxiosResponse<V>>(url, {
      params: resultParams,
      withCredentials,
    })
    const result = await pro
    return result
  }

  async post<V extends BaseResponse | any>(
    url: string,
    params: any,
    { withCredentials = true, contentType = RequestContentType.JSON }: MindRequestConfig = {},
  ): Promise<AxiosResponse<V>> {
    let resultParams = params
    if (contentType === RequestContentType.URL_ENCODE) {
      resultParams = qs.stringify(resultParams)
    }
    const httpHeader: any = {}
    if (contentType === RequestContentType.FORM_DATA) {
      httpHeader['Content-Type'] = formDataContentType
    }
    if (contentType === RequestContentType.FORM_URLENCODED) {
      httpHeader['Content-Type'] = formUrlencoded
      resultParams = qs.stringify(params)
    }
    const result = await this._ajax.post<V, AxiosResponse<V>>(url, resultParams, {
      withCredentials,
      headers: httpHeader,
    })
    return result
  }
}

export default new AjaxRequest({
  baseURL: import.meta.env.VITE_DEALS_API_DOMAIN,
})
